package com.zdy.ws.ws.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zdy.ws.ws.constant.MessageType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName zdy
 * @Description <类描述信息>
 * @Author LiWanLiang
 * @JDK 1.8
 */
@Slf4j
@Component
@ServerEndpoint("/customerService/{roomId}/{userId}")
public class WebSocketServer {


    /**
     * 当前会话
     */
    private Session session;


    String thisRoomId;

    String thisUserId;

    /**
     * 用来存放每个客户端对应的 WebSocketServer 对象
     */
    private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();

    /**
     * 将每个sessionId 和每个用户做绑定
     */
    private static ConcurrentHashMap<String, String> currentSessionUserMap = new ConcurrentHashMap<>();


    private static ConcurrentHashMap<String, List<Session>> roomSessions = new ConcurrentHashMap<>();


    private static ConcurrentHashMap<String, Set<String>> roomUsers = new ConcurrentHashMap<>();


    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId, @PathParam("roomId") String roomId) {
        this.session = session;
        log.info("客户端userId：{},与服务端成功建立连接", userId);


        log.info("用户:{} 上线了", userId);


        WebSocketServer oldSession = webSocketMap.put(userId, this);
        if (oldSession != null) {
            try {
                // 关闭之前的session
                Session beforeSession = oldSession.session;
                if (beforeSession != null) {
                    log.error("用户:{} 之前登录了,将之前的移除", userId);
                    String id = beforeSession.getId();
                    currentSessionUserMap.remove(id);
                    beforeSession.close();
                }
            } catch (IOException e) {
                log.error("关闭session异常:{}", e);
            }
        }
        // 将sessionId 和 当前的用户关联
        currentSessionUserMap.put(this.session.getId(), userId);


        List<Session> sessions = null;
        Set<String> users = null;
        if (!roomSessions.containsKey(roomId)) {
            roomSessions.put(roomId, sessions = new ArrayList<>());
        } else {
            sessions = roomSessions.get(roomId);
        }


        if (!roomUsers.containsKey(roomId)) {
            roomUsers.put(roomId, users = new HashSet<>());
        } else {
            users = roomUsers.get(roomId);
        }


        thisRoomId = roomId;
        thisUserId = userId;

        sessions.add(session);
        users.add(thisUserId);

        log.info("userId={}添加房间roomId={}", userId, roomId);

        Map<String, Object> response = new HashMap<>(2);
        response.put("success", Boolean.TRUE);
        response.put("type", MessageType.login.name());
        sendMessage(JSON.toJSONString(response), userId);

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("userId") String userId, @PathParam("roomId") String roomId) {
        if (StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)) {
            log.info("移除用户:{}的SocketServer", userId);
            webSocketMap.remove(userId);
        }

        if (session != null && currentSessionUserMap.containsKey(session.getId())) {
            log.info("移除用户session:{}的SocketServer", session.getId());
            currentSessionUserMap.remove(session.getId());
        }

        List<Session> sessions = null;
        Set<String> users = null;
        if (!roomSessions.containsKey(roomId)) {
            roomSessions.put(roomId, sessions = new ArrayList<>());
        } else {
            sessions = roomSessions.get(roomId);
        }


        if (!roomUsers.containsKey(roomId)) {
            roomUsers.put(roomId, users = new HashSet<>());
        } else {
            users = roomUsers.get(roomId);
        }


        if(!users.isEmpty()){
            users.remove(thisUserId);
            if(roomUsers.containsKey(roomId) && (users.isEmpty() || users.size() == 0)){
                log.info("移除roomUsers房间缓存roomId={}", roomId);
                //如果这个房间已经没有用户,就删除用户房间
                roomUsers.remove(roomId);
            }
        }




        if (!sessions.isEmpty()) {
            sessions.remove(session);
            if(roomSessions.containsKey(roomId) && (sessions.isEmpty()|| sessions.size() == 0)){
                //如果这个房间已经没有用户，就删除session房间
                log.info("移除roomSessions房间缓存roomId={}", roomId);
                roomSessions.remove(roomId);
            }
        }

        log.info("userId={}离开房间roomId={}", userId, roomId);
        log.info("客户端,与服务端成功关闭连接");
    }


    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        //log.info("接收到客户端报文:{}", message);
        if (StringUtils.isNotBlank(message)) {
            //TODO 心跳检测
            if(message.equals("ping")){
                sendMessage("pong", thisUserId);
                return;
            }
            JSONObject jsonData;
            try {
                jsonData = JSON.parseObject(message);
            } catch (Exception e) {
                log.error("解析客户端报文失败:{}", e);
                sendMessage(getErrorMessage(null));
                return;
            }

            Map<String, Object> response = new HashMap<>(8);
            MessageType messageType;
            String type = jsonData.getString("type");
            try {
                messageType = MessageType.valueOf(type);
            } catch (IllegalArgumentException e) {
                sendMessage(getErrorMessage(type));
                return;
            }

            String typeName = messageType.name();
            response.put("type", typeName);
            switch (messageType) {

                case offer:
                case answer:
                case candidate:
                    log.info("Sending {} to {}", typeName, thisRoomId);
                    response.put("data", jsonData.get("data"));
                    break;

                case callready:

                    Set<String> users =roomUsers.get(thisRoomId);
                    List<String> joinUser=new ArrayList<>();
                    if(users == null||users.isEmpty()){
                        log.info("该房间不存在roomId = {} ", thisRoomId);
                        return;
                    }

                    joinUser.addAll(users);
                    joinUser.remove(thisUserId);
                    if(joinUser.isEmpty()){
                        return;
                    }

                    response.clear();
                    response.put("type", MessageType.join.name());
                    response.put("joinIds", joinUser);
                    sendMessage(JSON.toJSONString(response), thisUserId);


                    return;

                case leave:
                    log.info("Disconnecting from {}", thisRoomId);
                    break;


                /**
                 * 以下为3d看车的socket
                 */
                case initialize:
                    processInit(session, response);
                    break;

                case C_reset:
                case C_color:
                case C_door:
                case C_dolly:
                case C_orbit:
                case C_onMouseMove:
                case C_onMouseWheel:
                case C_voice:
                    //语音
                    log.info("语音类型 from {}", messageType);
                    response.put("data", jsonData.get("data"));
                    response.put("type", messageType.name());
                    break;
                case C_lungu:

                    response.put("data", jsonData.get("data"));
                    response.put("type", messageType.name());

                    break;


                default:
                    sendMessage(getErrorMessage(type));
                    return;
            }
            sendRoomMessage(JSON.toJSONString(response), thisRoomId,thisUserId);
        }
    }


    public void processInit(Session session, Map<String, Object> response) {

        response.put("type", MessageType.C_reset);
        response.put("distance", 0);
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("服务端发生错误:{}", error);
    }

    /**
     * 服务器主动推送
     */
    public void sendMessage(String message) {
        try {
            if (session != null && this.session.isOpen()) {
                this.session.getBasicRemote().sendText(message);
            } else {
                log.error("session is closed !!!{}",thisUserId);
            }
        } catch (IOException e) {
            log.error("发送消息:{},到客户端异常:{}", message, e);
        }
    }

    /**
     * 服务器主动推送----给同个房间，其他用户推送消息
     */
    public void sendMessage(Session session, String message) {
        try {
            //控制多次并发发送
            synchronized(session) {
                if (session != null && session.isOpen()) {
                    session.getBasicRemote().sendText(message);
                    log.info("发送message !!!={}",message);
                } else {
                    log.error("session is closed !!!{}",thisUserId);
                }
            }
        } catch (IOException e) {
            log.error("发送消息:{},到客户端异常:{}", message, e);
        }
    }


    /**
     * 服务器主动推送
     */
    public void sendMessage(String message, String userId) {
        if (StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)) {
            webSocketMap.get(userId).sendMessage(message);
        } else {
            log.error("用户：{} 未上线!!!", userId);
        }
    }


    /**
     * 服务器主动推送--给同个房间，其他用户推送消息
     */
    public void sendRoomMessage(String message, String roomId,String userId) {
        if (StringUtils.isNotBlank(roomId) && roomSessions.containsKey(roomId)) {
            roomSessions.get(roomId).forEach((session1 -> {
                boolean isUserSession =  userId.equals(currentSessionUserMap.get(session1.getId()));
                boolean isSession =  session1.getId().equals(WebSocketServer.this.session.getId());
                if (!isUserSession && !isSession) {
                    sendMessage(session1, message);
                }

            }));
        } else {
            log.error("房间是空的：{} !!!", roomId);
        }
    }


    public String getErrorMessage(String type) {
        Map<String, Object> response = new HashMap<>(2);
        response.put("type", "error");
        response.put("message", "Command not found:" + type);
        return JSON.toJSONString(response);
    }

}
